package services;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import repositories.VecinoRepository;
import security.Authority;
import security.LoginService;
import security.UserAccount;
import domain.Acta;
import domain.Administrador;
import domain.ComunidadDeVecinos;
import domain.Cuota;
import domain.Mensaje;
import domain.Reunion;
import domain.Vecino;
import forms.VecinoForm;

@Service
@Transactional
public class VecinoService {

	// Managed Repository

	@Autowired
	private VecinoRepository vecinoRepository;
	// Supporting Services
	@Autowired
	private AdministradorService administradorService;

	@Autowired
	private CuotaService cuotaService;
	@Autowired
	private ComunidadDeVecinosService comunidadDeVecinosService;

	// Constructors

	public VecinoService() {
		super();
	}

	// Simple CRUD methods

	public Vecino create() {
		Vecino result;
		UserAccount userAccount;
		Authority authority;

		Collection<Cuota> cuotas;
		Collection<Reunion> reuniones;
		Collection<Mensaje> mensajes;
		Collection<ComunidadDeVecinos> comunidadesDeVecinos;
		Collection<ComunidadDeVecinos> comunidadesDeVecinosPresididas;
		Collection<Acta> actasMoroso;

		authority = new Authority();
		cuotas = new HashSet<Cuota>();
		reuniones = new HashSet<Reunion>();
		mensajes = new HashSet<Mensaje>();
		comunidadesDeVecinos = new HashSet<ComunidadDeVecinos>();
		comunidadesDeVecinosPresididas = new HashSet<ComunidadDeVecinos>();
		actasMoroso = new HashSet<Acta>();

		result = new Vecino();
		result.setCuotas(cuotas);
		result.setReuniones(reuniones);
		result.setMensajes(mensajes);
		result.setComunidadesDeVecinos(comunidadesDeVecinos);
		result.setComunidadesDeVecinosPresididas(comunidadesDeVecinosPresididas);
		result.setActasMoroso(actasMoroso);

		userAccount = new UserAccount();
		authority.setAuthority("VECINO");
		userAccount.addAuthority(authority);
		result.setUserAccount(userAccount);

		return result;

	}

	public void save(Vecino vecino) {
		Assert.notNull(vecino);
		Assert.notNull(vecino.getComunidadesDeVecinos());
		Assert.isTrue(administradorService.findByPrincipal() instanceof Administrador);

		Assert.isNull(administradorService.findByUsername(vecino
				.getUserAccount().getUsername()));

		vecinoRepository.save(vecino);
	}

	public void save(Vecino vecino, int comunidadId) {
		Assert.notNull(vecino);
		Assert.notNull(vecino.getComunidadesDeVecinos());
		Assert.isTrue(administradorService.findByPrincipal() instanceof Administrador);

		Assert.isNull(administradorService.findByUsername(vecino
				.getUserAccount().getUsername()));

		Collection<String> viviendasOcupadas;
		viviendasOcupadas = comunidadDeVecinosService
				.findViviendasOcupadas(comunidadId);
		Assert.isTrue(!viviendasOcupadas.contains(vecino.getVivienda()));

		vecinoRepository.save(vecino);
	}

	public Vecino findOne(int vecinoId) {
		Vecino result;

		result = vecinoRepository.findOne(vecinoId);

		return result;
	}

	// Other Business Methods
	public Vecino findByPrincipal() {
		Vecino result;
		UserAccount userAccount;

		userAccount = LoginService.getPrincipal();
		Assert.notNull(userAccount);

		result = findByUserAccount(userAccount);
		Assert.notNull(result);

		return result;
	}

	public Vecino findByUserAccount(UserAccount userAccount) {
		Assert.notNull(userAccount);
		Vecino result;

		result = vecinoRepository.findByUserAccountId(userAccount.getId());

		return result;
	}

	public Vecino findByUsername(String username) {
		Assert.notNull(username);
		Vecino result;
		result = vecinoRepository.findByUsername(username);
		return result;
	}

	public Collection<Vecino> findByCommunity(ComunidadDeVecinos cv) {
		Assert.notNull(cv);
		Assert.isTrue(administradorService.findByPrincipal() instanceof Administrador);
		Collection<Vecino> res;

		res = vecinoRepository.findByCommunity(cv.getId());

		return res;
	}

	// Form Methods
	public VecinoForm vecinoToForm(Vecino v) {
		Assert.notNull(v);
		VecinoForm result;

		result = new VecinoForm();

		result.setVecinoId(v.getId());
		result.setComunidadId(0);

		result.setDni(v.getDni());
		result.setNombre(v.getNombre());
		result.setApellidos(v.getApellidos());
		result.setEmail(v.getEmail());
		result.setVivienda(v.getVivienda());
		result.setMoroso(v.getMoroso());

		result.setPassword(v.getUserAccount().getPassword());
		result.setUsername(v.getUserAccount().getUsername());

		return result;
	}

	public Vecino reconstruct(VecinoForm vecinoForm) {
		Assert.notNull(vecinoForm);
		Vecino result;
		ComunidadDeVecinos cv;
		Md5PasswordEncoder encoder = new Md5PasswordEncoder();
		String password = vecinoForm.getDni();
		String hash = encoder.encodePassword(password, null);

		if (vecinoForm.getVecinoId() == 0) {
			result = create();
			result.getUserAccount().setUsername(vecinoForm.getDni());
			result.getUserAccount().setPassword(hash);
			result.setNombre(vecinoForm.getNombre());
			result.setApellidos(vecinoForm.getApellidos());
			result.setEmail(vecinoForm.getEmail());
			result.setVivienda(vecinoForm.getVivienda());
			result.setDni(vecinoForm.getDni());
			result.setMoroso(vecinoForm.getMoroso());

			cv = comunidadDeVecinosService.findOne(vecinoForm.getComunidadId());
			result.getComunidadesDeVecinos().add(cv);
		} else {
			result = findOne(vecinoForm.getVecinoId());
			result.setEmail(vecinoForm.getEmail());
		}

		return result;
	}

	public Collection<Vecino> findAll() {
		Collection<Vecino> res;
		res = vecinoRepository.findAll();
		return res;
	}

	public Collection<Vecino> findByMorosos() {
		Collection<Vecino> res;
		Assert.isTrue(administradorService.findByPrincipal() instanceof Administrador);

		res = vecinoRepository.findByMorosos();
		return res;
	}

	public Collection<Vecino> findByMorososByComunidad(int comunidadId) {
		Collection<Vecino> res;
		res = new ArrayList<Vecino>();
		Collection<Vecino> vecinos;
		Date fechaActual;
		Date fechaLimite;
		Boolean algunaCoutaNoPagada;

		fechaActual = new Date();
		vecinos = vecinoRepository.findByMorososByComunidad(comunidadId);

		for (Vecino v : vecinos) {
			Collection<Cuota> cuotas;

			algunaCoutaNoPagada = false;
			cuotas = cuotaService.findCuotasNoPagadasByVecinoYComunidad(
					v.getId(), comunidadId);

			for (Cuota c : cuotas) {

				Calendar calendar = Calendar.getInstance();
				calendar.setTime(c.getFecha());
				calendar.add(Calendar.MONTH, +2);
				fechaLimite = calendar.getTime();

				if (!fechaActual.before(fechaLimite)) {
					algunaCoutaNoPagada = true;
				}
			}
			if (algunaCoutaNoPagada) {
				if (!res.contains(v)) {
					res.add(v);
				}
			}
		}

		return res;
	}

	public Collection<Vecino> finByNoMorosos() {
		Collection<Vecino> res;
		Assert.isTrue(administradorService.findByPrincipal() instanceof Administrador);

		res = vecinoRepository.findByNoMorosos();
		return res;
	}

	public void hacerMoroso(int morosoId) {
		Vecino vecinoPersistido;
		Date fechaLimite;
		Date fechaActual;
		Vecino moroso;
		Boolean algunaCoutaNoPagada;

		Assert.isTrue(administradorService.findByPrincipal() instanceof Administrador);

		algunaCoutaNoPagada = false;
		moroso = findOne(morosoId);
		Assert.notNull(moroso);
		vecinoPersistido = findOne(moroso.getId());

		Collection<Cuota> cuotas;
		cuotas = cuotaService.findCuotasNoPagadasByAdmin(morosoId);

		fechaActual = new Date();

		for (Cuota c : cuotas) {

			Calendar calendar = Calendar.getInstance();
			calendar.setTime(c.getFecha());
			calendar.add(Calendar.MONTH, +2);
			fechaLimite = calendar.getTime();

			if (!fechaActual.before(fechaLimite)) {
				algunaCoutaNoPagada = true;
			}
		}
		Assert.isTrue(algunaCoutaNoPagada);
		vecinoPersistido.setMoroso(true);
		save(vecinoPersistido);
	}

	public void quitarMorosidad(int morosoId) {
		Vecino moroso;
		Date fechaActual;
		Date fechaLimite;
		Collection<Cuota> cuotas;

		Assert.isTrue(administradorService.findByPrincipal() instanceof Administrador);

		moroso = findOne(morosoId);
		Assert.notNull(moroso);

		cuotas = cuotaService.findCuotasNoPagadasByAdmin(morosoId);

		fechaActual = new Date();

		for (Cuota c : cuotas) {

			Calendar calendar = Calendar.getInstance();
			calendar.setTime(c.getFecha());
			calendar.add(Calendar.MONTH, +2);
			fechaLimite = calendar.getTime();

			Assert.isTrue(fechaActual.before(fechaLimite));
		}
		moroso.setMoroso(false);
		save(moroso);

	}

	public void hacerPresidente(int comunidadId, int presidenteId) {

		ComunidadDeVecinos cv;
		Vecino presidente;

		Assert.isTrue(administradorService.findByPrincipal() instanceof Administrador);

		cv = comunidadDeVecinosService.findOne(comunidadId);
		presidente = findOne(presidenteId);
		Assert.notNull(cv);
		Assert.notNull(presidente);
		cv.setPresidente(presidente);
		comunidadDeVecinosService.save(cv);

	}

	public Collection<Vecino> findAsistentesByReunion(int reunionId) {

		Collection<Vecino> asistentes;
		asistentes = vecinoRepository.findAsistentesByReunion(reunionId);
		return asistentes;

	}

	public int findPrincipalId() {
		int res;
		Vecino principal;
		principal = findByPrincipal();
		res = principal.getId();
		return res;
	}

	public void delete(Vecino v) {
		Assert.notNull(v);
		vecinoRepository.delete(v);

	}

	public Collection<Vecino> findByNotCommunity(int comunidadId) {
		Collection<Vecino> res;
		Collection<Vecino> vecinosDeLaComunidad;
		vecinosDeLaComunidad = vecinoRepository.findByCommunity(comunidadId);
		res = vecinoRepository.findByNotCommunity(comunidadId);
		res.removeAll(vecinosDeLaComunidad);

		return res;
	}

}
